import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class Linpack extends Benchmark {

    private int Interactions = 2;
    private static int Max_Instances = 30;
    private static int Instances[] = new int[Max_Instances / 2 + 1];

    public Linpack(List<String> Ls) 
    {	super(Ls);
	ParseArgs(Ls);
	MakeInstancesArray(TestingSystem.getNuCores());    }

    public static int getMax_Instances() 
    {	return Max_Instances;    }

    public static void setMax_Instances(int mInstances) 
    {	Max_Instances = mInstances;    }

    public static int[] getInstances() 
    {	return Linpack.Instances;    }

    public static void setInstances(int[] instances) 
    {	Linpack.Instances = instances;    }

    public int getIteractions() 
    {	return Interactions;    }

    public void setInteractions(int Intera) 
    {	Interactions = Intera;    }

    private static void MakeInstancesArray(int NuCores) 
    {
	if (NuCores < Max_Instances) 
		{    Max_Instances = NuCores;	}
		Instances[0] = 1;
	
	for (int value = 2, i = 1; value <= Max_Instances; value = value + 2, i++) 
		{    Linpack.Instances[i] = value;	}
    }

    public static void PrintUsage() 
    {	System.out.println("You entered wrong parameters! Correct syntax of command is: ");
    	System.out.println("java runtest -t <testname> -i <numbers of interactions> -m <max number of linpack instances.");	}

    public boolean isParsableToInt(String i) 
    {
	try 
	{   Integer.parseInt(i);
	    return true;	} 
	catch (NumberFormatException nfe) 
	{   return false;	}
    }

    public int ParseToInt(String i) 
    {
	try 
	{   int integ = Integer.parseInt(i);
	    return integ;	} 
	catch (NumberFormatException nfe) 
	{   PrintUsage();
	    return -1;		}
    }

    public void ParseArgs(List<String> Ls) 
    {
	int iterIndex = Ls.indexOf("-i");

	if (iterIndex >= 0) 
	{
	    int iterValue = ParseToInt(Ls.get(iterIndex + 1));

	    setInteractions(iterValue);
	    Ls.remove(iterIndex);
	    Ls.remove(iterIndex);
	}

	int maxInstIndex = Ls.indexOf("-m");

	if (maxInstIndex >= 0) 
	{
	    int maxInstValue = ParseToInt(Ls.get(maxInstIndex + 1));

	    setMax_Instances(maxInstValue);
	    Ls.remove(maxInstIndex);
	    Ls.remove(maxInstIndex);
	}

	if (!Ls.isEmpty()) 
	{   PrintUsage();
	    System.exit(1);	}

    } 

    public void PrintTestInfo(LinpackStreamTestMode tm, int inst, int inter) {

    }

    public static List<Cpu> GetListOfCpuToTest(int inst) 
    {
	
	String[] CommandStringHwLock = { "hwloc-distrib", Integer.toString( inst ) };
	String hwlockDistrib = FileOutput.RunCommand( false, CommandStringHwLock );

	String[] CommandStringHwLockCalc = { "hwloc-calc", "--physical-output", "--logical-input", "--proclist", hwlockDistrib };
	String hwlockCalc = FileOutput.RunCommand( false, CommandStringHwLockCalc );

	List<Cpu> CpuList = new ArrayList<>();
	String[] st = hwlockCalc.split(",");

	for (String St : st) 
	{    CpuList.add(new Cpu(Integer.parseInt(St)));	
		System.out.println("Parm for CPU consturctor is "  + St);
	}

	return CpuList;
    }

    public List<RThreads> ExecuteBenchmark(List<Cpu> CpuList, LinpackStreamTestMode tm, String SemId) throws IOException {
	
	String BinaryNameToExecute = this.getPathToBinary();
	List<RThreads> ThreadsL = new ArrayList<>();
	RThreads RT;

	for (Cpu cpu : CpuList) {

	    if (tm == LinpackStreamTestMode.TaskSet) 
	    {	String[] Command = { "taskset", cpu.GetCoreMask(), BinaryNameToExecute, SemId };  
		RT = new RThreads( true, Command );    }

	    else if (tm == LinpackStreamTestMode.Numa) 
	    {	String[] Command = { "numactl", "--cpunodebind=" + Integer.toString(cpu.GetNumaNode()), SemId };
		RT = new RThreads( true, Command );    }

	    else 
	    {	String[] Command = { BinaryNameToExecute , SemId };
		RT = new RThreads( true, Command );    }

	    ThreadsL.add(RT);
	}
	return ThreadsL;
    }

    public List<int[]> GetJobNumbersPerCore( List<Cpu> lCpu )
    {
	List<int[]> lOutput = new ArrayList<>();
	int[] cpuOutput = new int[3];
	
	for ( Cpu cpu : lCpu )
	{	cpuOutput = cpu.getCpuUsage().ReadProcStatValues( cpu.GetCoreNumber() );
	cpuOutput = cpu.getCpuUsage().GetProcStatDiff( cpu.GetCoreNumber() );
	
	lOutput.add( cpuOutput );	}
	
	return lOutput;
	
    }
    
    public BufferedWriter CreateFileForOutput( LinpackStreamTestMode tm,  int instances, int iteraction, String prefixName ) {

	String BenchmarkName = this.getClass().getName();
	String prefString;
	if ( prefixName != null )
	{	prefString = prefixName + "-";	}
	else
	{	prefString = "";	}
	
	String OutputFilenameString = "/home/kkolakow/tmp/" + prefString + BenchmarkName + "-NuRunningInstances-" + instances + "-NuIteraction-" + iteraction;
	BufferedWriter out = null;
	
	try 
	{    out = new BufferedWriter(new FileWriter(OutputFilenameString));	} 
	catch (IOException e) 
	{    e.printStackTrace();	}

	return out;
    }

    
    @Override
    public void RunBenchmark() 
    {

	int iteractions = getIteractions();
	int[] instanceArray = getInstances();
	LinpackStreamTestMode[] TM = LinpackStreamTestMode.values();
	List<Cpu> CpuToRunTest;
	String SemIdString;
	
	List<RThreads> RunnTL = new ArrayList<>();

	for (LinpackStreamTestMode tm : TM) {
	    
	    for (int inst : instanceArray) {
		for (int iter = 1; iter <= iteractions; iter++) {
		    PrintTestInfo( tm, inst, iter );
		    CpuToRunTest = GetListOfCpuToTest( inst );
		    String instString = Integer.toString( inst );
		    
		    String[] commandArgsCreate = "/home/kkolakow/git/performance-tests/Performance-testings/src/sem_progs/semcreate.x86_64".split("\\s+");
		    SemIdString = FileOutput.RunCommand( true, commandArgsCreate );   
		    
		    try 
		    {	RunnTL = ExecuteBenchmark( CpuToRunTest, tm, SemIdString );    }
		    catch (Exception e)
		    {	e.printStackTrace();	    }
		    
		    String[] commandArgswait = { "/home/kkolakow/git/performance-tests/Performance-testings/src/sem_progs/semwaitforpids.x86_64", SemIdString, instString };
		    FileOutput.RunCommand( true, commandArgswait );
		    
		    String[] commandArgsRemove = { "/home/kkolakow/git/performance-tests/Performance-testings/src/sem_progs/semremove.x86_64", SemIdString, instString };
		    FileOutput.RunCommand( true, commandArgsRemove );

		    BufferedWriter benchmarkOutput = CreateFileForOutput( tm, inst, iter, null );
		    BufferedWriter mpstatOutput = CreateFileForOutput( tm, inst, iter, "mpstat" );
		    
		    try 
		    {
			for ( RThreads RT : RunnTL ) 
			{
			    while ( !RT.isFinished() )
			    {	TestingSystem.GetProcessorsUsage( mpstatOutput );
			    	Thread.sleep(3000);	}

			    benchmarkOutput.write( RT.getOutputString() );
			    benchmarkOutput.flush();	
			    
			    RunnTL.remove( RT );
			}

			benchmarkOutput.close();
		    }
		    catch ( Exception e ) 
		    {	e.printStackTrace();    }
		       
		}
	    }
	}
    }

    @Override
    public int AnalyseResults() {
	// TODO Auto-generated method stub
	return 0;
    }

	public void DownloadPackages()
	{
		String[] command1 = "yum -y install sysstat".split("\\s+");
		String[] command2 = "rpm -ivh http://download.devel.redhat.com/brewroot/packages/hwloc/1.1/0.1.el5/x86_64/hwloc-1.1-0.1.el5.x86_64.rpm".split("\\s+");		

		try 
		{   new RThreads( true, command1 );
		    new RThreads( true, command2 );	} 
		catch (IOException e) 
		{   e.printStackTrace();		}	
		
	}

    }


